package com.wmada.toolkit.rest;

import com.wmada.toolkit.common.TimeUtils;
import com.wmada.toolkit.rest.builder.HeaderBuilder;
import com.wmada.toolkit.rest.builder.ParamBuilder;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.StandardHttpRequestRetryHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Closeable;
import java.io.IOException;
import java.util.Map;

/**
 * Downloader工具类
 *
 * @author will.ma
 * @date 2019-4-25
 */
@SuppressWarnings("unused")
public class DownloadProxy implements Closeable {

    private static final Logger LOGGER = LoggerFactory.getLogger(DownloadProxy.class);

    /**
     * 编码格式，默认UTF-8
     */
    private String encoding = "UTF-8";

    /**
     * 连接超时时间，单位毫秒。
     */
    private int connectTimeout = (int) (5 * TimeUtils.SECOND_IN_MILLISECONDS);

    /**
     * 响应超时时间，单位毫秒。
     */
    private int socketTimeout = (int) (5 * TimeUtils.SECOND_IN_MILLISECONDS);

    /**
     * setConnectTimeout：设置连接超时时间，单位毫秒。
     * setConnectionRequestTimeout：设置从connect Manager(连接池)获取Connection
     * 超时时间，单位毫秒。这个属性是新加的属性，因为目前版本是可以共享连接池的。
     * setSocketTimeout：请求获取数据的超时时间(即响应时间)，单位毫秒。 如果访问一个接口，多少时间内无法返回数据，就直接放弃此次调用。
     */
    private RequestConfig requestConfig = RequestConfig.custom()
                                                       .setConnectionRequestTimeout(connectTimeout)
                                                       .setConnectTimeout(connectTimeout)
                                                       .setSocketTimeout(socketTimeout)
                                                       .build();

    private final CloseableHttpClient httpClient;

    public DownloadProxy(int maxRetries) {
        if (maxRetries == 0) {
            httpClient = HttpClients.custom().disableAutomaticRetries().build();
        } else {
            httpClient = HttpClients.custom().setRetryHandler(new StandardHttpRequestRetryHandler(maxRetries, false)).build();
        }
    }

    public DownloadProxy(CloseableHttpClient client) {
        httpClient = client;
    }

    public DownloadProxy() {
        httpClient = HttpClients.createDefault();
    }

    @Override
    public void close() throws IOException {
        httpClient.close();
    }

    public String getEncoding() {
        return encoding;
    }

    public DownloadProxy setEncoding(String encoding) {
        this.encoding = encoding;
        return this;
    }

    public int getConnectTimeout() {
        return connectTimeout;
    }

    public DownloadProxy setConnectTimeout(int connectTimeout) {
        this.connectTimeout = connectTimeout;
        updateRequestConfig();
        return this;
    }

    public int getSocketTimeout() {
        return socketTimeout;
    }

    public DownloadProxy setSocketTimeout(int socketTimeout) {
        this.socketTimeout = socketTimeout;
        updateRequestConfig();
        return this;
    }

    public RequestConfig getRequestConfig() {
        return requestConfig;
    }

    public DownloadProxy setRequestConfig(RequestConfig requestConfig) {
        this.requestConfig = requestConfig;
        return this;
    }

    private void updateRequestConfig() {
        requestConfig = RequestConfig.custom()
                                     .setConnectionRequestTimeout(connectTimeout)
                                     .setConnectTimeout(connectTimeout)
                                     .setSocketTimeout(socketTimeout)
                                     .build();
    }

    /**
     * Description: 获得响应结果
     *
     * @param request Request.
     * @return Response
     */
    public Response request(HttpRequestBase request) throws DownloadException {
        // 执行请求
        try (CloseableHttpResponse response = httpClient.execute(request)) {
            // 获取返回结果
            if (response != null && response.getStatusLine() != null) {
                return new Response(request, response);
            } else {
                throw new DownloadException(request, DownloadException.STATUS_REQUEST_ERROR);
            }
        } catch (IOException e) {
            throw new DownloadException(e, request, DownloadException.STATUS_REQUEST_ERROR);
        }
    }

    /**
     * 发送head请求；带请求参数
     *
     * @param url     请求地址
     * @param params  请求参数集合
     * @param headers 请求头集合
     * @return Response
     */
    public Response head(String url, Map<String, String[]> params, Header[] headers) {
        try {
            // 创建http对象
            HttpHead request = new HttpHead(ParamBuilder.buildUrl(url, params));
            request.setConfig(requestConfig);
            request.setHeaders(headers);
            return request(request);
        } catch (DownloadException e) {
            LOGGER.warn(e.getMessage(), e);
            return e.getResponse();
        }
    }

    /**
     * 发送options请求；带请求参数
     *
     * @param url     请求地址
     * @param params  请求参数集合
     * @param headers 请求头集合
     * @return Response
     */
    public Response options(String url, Map<String, String[]> params, Header[] headers) {
        try {
            // 创建http对象
            HttpOptions request = new HttpOptions(ParamBuilder.buildUrl(url, params));
            request.setConfig(requestConfig);
            request.setHeaders(headers);
            return request(request);
        } catch (DownloadException e) {
            LOGGER.warn(e.getMessage(), e);
            return e.getResponse();
        }
    }

    /**
     * 发送get请求；带请求头和请求参数
     *
     * @param url     请求地址
     * @param params  请求参数集合
     * @param headers 请求头集合
     * @return Response
     */
    public Response get(String url, Map<String, String[]> params, Header[] headers) {
        try {
            // 创建http对象
            HttpGet request = new HttpGet(ParamBuilder.buildUrl(url, params));
            request.setConfig(requestConfig);
            request.setHeaders(headers);
            return request(request);
        } catch (DownloadException e) {
            LOGGER.warn(e.getMessage(), e);
            return e.getResponse();
        }
    }

    /**
     * 发送put请求；带请求头和请求参数
     *
     * @param url     请求地址
     * @param params  请求参数集合
     * @param headers 请求头集合
     * @param entity  请求体
     * @return Response
     */
    public Response put(String url, Map<String, String[]> params, Header[] headers, HttpEntity entity) {
        try {
            // 创建http对象
            HttpPut request = new HttpPut(ParamBuilder.buildUrl(url, params));
            request.setConfig(requestConfig);
            request.setEntity(entity);
            request.setHeaders(headers);
            return request(request);
        } catch (DownloadException e) {
            LOGGER.warn(e.getMessage(), e);
            return e.getResponse();
        }
    }

    /**
     * 发送post请求；带请求头和请求参数
     *
     * @param url     请求地址
     * @param params  请求参数集合
     * @param headers 请求头集合
     * @param entity  请求体
     * @return Response
     */
    public Response post(String url, Map<String, String[]> params, Header[] headers, HttpEntity entity) {
        try {
            // 创建http对象
            HttpPost request = new HttpPost(ParamBuilder.buildUrl(url, params));
            request.setConfig(requestConfig);
            request.setEntity(entity);
            request.setHeaders(headers);
            return request(request);
        } catch (DownloadException e) {
            LOGGER.warn(e.getMessage(), e);
            return e.getResponse();
        }
    }

    /**
     * 发送patch请求；带请求头和请求参数
     *
     * @param url     请求地址
     * @param params  请求参数集合
     * @param headers 请求头集合
     * @param entity  请求体
     * @return Response
     */
    public Response patch(String url, Map<String, String[]> params, Header[] headers, HttpEntity entity) {
        try {
            // 创建http对象
            HttpPatch request = new HttpPatch(ParamBuilder.buildUrl(url, params));
            request.setConfig(requestConfig);
            request.setEntity(entity);
            request.setHeaders(headers);
            return request(request);
        } catch (DownloadException e) {
            LOGGER.warn(e.getMessage(), e);
            return e.getResponse();
        }
    }

    /**
     * 发送delete请求；带请求参数
     *
     * @param url     请求地址
     * @param params  请求参数集合
     * @param headers 请求头集合
     * @return Response
     */
    public Response delete(String url, Map<String, String[]> params, Header[] headers) {
        try {
            // 创建http对象
            HttpDelete request = new HttpDelete(ParamBuilder.buildUrl(url, params));
            request.setConfig(requestConfig);
            request.setHeaders(headers);
            return request(request);
        } catch (DownloadException e) {
            LOGGER.warn(e.getMessage(), e);
            return e.getResponse();
        }
    }

    /**
     * 发送head请求；不带请求头和请求参数
     *
     * @param url 请求地址
     * @return Response
     */
    public Response head(String url) {
        return head(url, null, (Header[]) null);
    }

    /**
     * 发送head请求；带请求参数
     *
     * @param url    请求地址
     * @param params 请求参数集合
     * @return Response
     */
    public Response head(String url, Map<String, String[]> params) {
        return head(url, params, (Header[]) null);
    }

    /**
     * 发送head请求；带请求参数
     *
     * @param url     请求地址
     * @param params  请求参数集合
     * @param headers 请求头集合
     * @return Response
     */
    public Response head(String url, Map<String, String[]> params, Map<String, String> headers) {
        return head(url, params, HeaderBuilder.build(headers));
    }

    /**
     * 发送options请求；不带请求头和请求参数
     *
     * @param url 请求地址
     * @return Response
     */
    public Response options(String url) {
        return options(url, null, (Header[]) null);
    }

    /**
     * 发送options请求；带请求参数
     *
     * @param url    请求地址
     * @param params 请求参数集合
     * @return Response
     */
    public Response options(String url, Map<String, String[]> params) {
        return options(url, params, (Header[]) null);
    }

    /**
     * 发送options请求；带请求参数
     *
     * @param url     请求地址
     * @param params  请求参数集合
     * @param headers 请求头集合
     * @return Response
     */
    public Response options(String url, Map<String, String[]> params, Map<String, String> headers) {
        return options(url, params, HeaderBuilder.build(headers));
    }

    /**
     * 发送get请求；不带请求头和请求参数
     *
     * @param url 请求地址
     * @return Response
     */
    public Response get(String url) {
        return get(url, null, (Header[]) null);
    }

    /**
     * 发送get请求；带请求参数
     *
     * @param url    请求地址
     * @param params 请求参数集合
     * @return Response
     */
    public Response get(String url, Map<String, String[]> params) {
        return get(url, params, (Header[]) null);
    }

    /**
     * 发送get请求；带请求参数
     *
     * @param url     请求地址
     * @param params  请求参数集合
     * @param headers 请求头集合
     * @return Response
     */
    public Response get(String url, Map<String, String[]> params, Map<String, String> headers) {
        return get(url, params, HeaderBuilder.build(headers));
    }

    /**
     * 发送put请求；不带请求头和请求参数
     *
     * @param url    请求地址
     * @param entity 请求体
     * @return Response
     */
    public Response put(String url, HttpEntity entity) {
        return put(url, null, (Header[]) null, entity);
    }

    /**
     * 发送put请求；不带请求头和请求参数
     *
     * @param url    请求地址
     * @param params 请求参数集合
     * @param entity 请求体
     * @return Response
     */
    public Response put(String url, Map<String, String[]> params, HttpEntity entity) {
        return put(url, params, (Header[]) null, entity);
    }

    /**
     * 发送put请求；不带请求头和请求参数
     *
     * @param url     请求地址
     * @param params  请求参数集合
     * @param headers 请求头集合
     * @param entity  请求体
     * @return Response
     */
    public Response put(String url, Map<String, String[]> params, Map<String, String> headers, HttpEntity entity) {
        return put(url, params, HeaderBuilder.build(headers), entity);
    }

    /**
     * 发送post请求；不带请求头和请求参数
     *
     * @param url    请求地址
     * @param entity 请求体
     * @return Response
     */
    public Response post(String url, HttpEntity entity) {
        return post(url, null, (Header[]) null, entity);
    }

    /**
     * 发送post请求；不带请求头和请求参数
     *
     * @param url    请求地址
     * @param params 请求参数集合
     * @param entity 请求体
     * @return Response
     */
    public Response post(String url, Map<String, String[]> params, HttpEntity entity) {
        return post(url, params, (Header[]) null, entity);
    }

    /**
     * 发送post请求；不带请求头和请求参数
     *
     * @param url     请求地址
     * @param params  请求参数集合
     * @param headers 请求头集合
     * @param entity  请求体
     * @return Response
     */
    public Response post(String url, Map<String, String[]> params, Map<String, String> headers, HttpEntity entity) {
        return post(url, params, HeaderBuilder.build(headers), entity);
    }

    /**
     * 发送patch请求；不带请求头和请求参数
     *
     * @param url    请求地址
     * @param entity 请求体
     * @return Response
     */
    public Response patch(String url, HttpEntity entity) {
        return patch(url, null, (Header[]) null, entity);
    }

    /**
     * 发送patch请求；不带请求头和请求参数
     *
     * @param url    请求地址
     * @param params 请求参数集合
     * @param entity 请求体
     * @return Response
     */
    public Response patch(String url, Map<String, String[]> params, HttpEntity entity) {
        return patch(url, params, (Header[]) null, entity);
    }

    /**
     * 发送patch请求；不带请求头和请求参数
     *
     * @param url     请求地址
     * @param params  请求参数集合
     * @param headers 请求头集合
     * @param entity  请求体
     * @return Response
     */
    public Response patch(String url, Map<String, String[]> params, Map<String, String> headers, HttpEntity entity) {
        return patch(url, params, HeaderBuilder.build(headers), entity);
    }

    /**
     * 发送delete请求；不带请求头和请求参数
     *
     * @param url 请求地址
     * @return Response
     */
    public Response delete(String url) {
        return delete(url, null, (Header[]) null);
    }

    /**
     * 发送delete请求；带请求参数
     *
     * @param url    请求地址
     * @param params 请求参数集合
     * @return Response
     */
    public Response delete(String url, Map<String, String[]> params) {
        return delete(url, params, (Header[]) null);
    }

    /**
     * 发送delete请求；带请求参数
     *
     * @param url     请求地址
     * @param params  请求参数集合
     * @param headers 请求头集合
     * @return Response
     */
    public Response delete(String url, Map<String, String[]> params, Map<String, String> headers) {
        return delete(url, params, HeaderBuilder.build(headers));
    }

}
